<template>
  <div ref="gantt" :style="'height:'+ganttHeight+'px;width:100%'"></div>
</template>

<script>
  import { gantt } from 'dhtmlx-gantt'
  // import 'dhtmlx-gantt/codebase/skins/dhtmlxgantt_meadow.css'
  import '@/assets/public/dhtmlxgantt_cscec.css'
  import '../gantt/locale_cn'
  export default {
    name: 'Gantt',
    props: {
      tasks: {
        type: Object,
        default () {
          return { data: [], links: [] }
        }
      },plugins:{
        type:Object,
        default(){
          return {}
        }
      },columns:{
        type: Array,
        default(){
          return []
        }
      },gridWidth:{
        type:Number,
        default(){
          return 500
        }
      },height:{
        type:Number,
        default(){
          return 661
        }
      }
    },
    data(){
      return {
        ganttGridWidth: 500,
        ganttHeight:661,
        columnsData:[],
        taskData: { data: [], links: [] },
        cascadeAction:{
          indent: true,
          outdent: true,
          del: true
        },
        singularAction : {
          undo: true,
          redo: true
        },
        actions: {
          undo: function() {
            try {
              gantt.ext.undo.undo()
            }catch (e) {

            }
          },
          redo: function() {
            try {
              gantt.ext.undo.redo()
            }catch (e) {
            }
          },
          indent: function indent(task_id) {
            let prev_id = gantt.getPrevSibling(task_id)
            while (gantt.isSelectedTask(prev_id)) {
              const prev = gantt.getPrevSibling(prev_id)
              if (!prev) {
                break;
              }
              prev_id = prev;
            }
            if (prev_id) {
              const new_parent = gantt.getTask(prev_id)
              gantt.moveTask(task_id, gantt.getChildren(new_parent.id).length, new_parent.id);
              new_parent.type = gantt.config.types.project;
              new_parent.$open = true;
              gantt.updateTask(task_id);
              gantt.updateTask(new_parent.id);
            }
            return task_id
          },
          outdent: function outdent(task_id, initialIndexes, initialSiblings) {
            const cur_task = gantt.getTask(task_id)
            const old_parent = cur_task.parent
            if (gantt.isTaskExists(old_parent) && old_parent !== gantt.config.root_id) {
              let index = gantt.getTaskIndex(old_parent) + 1
              const prevSibling = initialSiblings[task_id].first

              if(gantt.isSelectedTask(prevSibling)){
                index += (initialIndexes[task_id] - initialIndexes[prevSibling]);
              }
              gantt.moveTask(task_id, index, gantt.getParent(cur_task.parent));
              if (!gantt.hasChild(old_parent))
              {
                gantt.getTask(old_parent).type = gantt.config.types.task;
              }
              gantt.updateTask(task_id);
              gantt.updateTask(old_parent);
            }
            return task_id
          },
          del: function(task_id) {
            if (gantt.isTaskExists(task_id)) {
              gantt.deleteTask(task_id)
            }
            return task_id
          },
          moveForward: (task_id) => {
            this.shiftTask(task_id, 1)
          },
          moveBackward: (task_id) => {
            this.shiftTask(task_id, -1)
          },
          moveUp:(task_id)=>{
            this.moveTask(task_id,-1)
          },
          moveDown:(task_id)=>{
            this.moveTask(task_id,1)
          }
        }
      }
    },
    watch:{
      columns:{
        handler(nv) {
          this.columnsData = nv && nv.map(item => {
            item.onrender = (task, node) => {
              node.setAttribute("title", task.text)
            }
            return item
          }) || []
        },
        immediate: true,
        deep: true
      },
      gridWidth:{
        handler(nv) {
          this.ganttGridWidth = nv
        },
        immediate: true,
      },
      height:{
        handler(nv){
          this.ganttHeight = nv
        },
        immediate: true
      },
      tasks:{
        handler(nv) {
          this.taskData = nv
        },
        immediate: true,
        deep: true
      }
    },
    methods:{
      /**
       * 字符长度超出20时，用悬浮文本显示
       */
      resolveTemplateText(text) {
        if(!text || !text.length) return ''
        if(text.length > 20) {
          return `<span title=${text} >${text.substring(0, 20)}...<span>`
        } else {
          return text
        }
      },
      ganttConfig(){
        gantt.plugins(this.plugins)
        const weekScaleTemplate = function (date) {
          const dateToStr = gantt.date.date_to_str("%d");
          const endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day");
          return dateToStr(date) + " - " + dateToStr(endDate);
        };
        gantt.config.scales = [
          {
            unit: 'year',
            step: 1,
            date: '%Y 年'
          },
          {
            unit: 'month',
            step: 1,
            date: '%M'
          },
          {unit: "week", step: 1,format: weekScaleTemplate}
          // {unit: "day", step: 1, format: "%m-%d"}
        ];
        gantt.config.autofit = true;
        gantt.config.drag_timeline = {
          ignore:".gantt_task_line, .gantt_task_link",
          useKey: false
        };
        gantt.config.auto_scheduling = true;
        gantt.config.auto_scheduling_strict = true;
        gantt.config.work_time = true;
        gantt.config.open_split_tasks = true;
        gantt.config.order_branch = true;
        gantt.config.order_branch_free = true;
        gantt.config.types= { task:'task',project:'project',milestone:'milestone',
          placeholder:'placeholder',subproject:'subproject'}
        gantt.config.xml_date = '%Y-%m-%d'
        gantt.config.undo = true;
        gantt.config.redo = true;
        gantt.config.undo_actions = {
          update:"update",
          remove:"remove", // remove an item from datastore
          add:"add"
        };
        gantt.config.undo_types = {
          link:"link",
          task:"task"
        };
        gantt.config.drag_timeline = {
          ignore:".gantt_task_line, .gantt_task_link",
          useKey: false
        };
        gantt.config.row_height = 20;
        gantt.config.min_column_width = 50;

        gantt.templates.timeline_cell_class = function (item, date) {
          if (date.getDay() === 0 || date.getDay() === 6) {
            return "weekend";
          }
        };
        gantt.config.grid_width = this.ganttGridWidth;
        gantt.config.date_grid = "%Y-%m-%d";
        gantt.config.scale_height = 60;
        gantt.config.font_width_ratio = 7;
        gantt.config.columns = this.columnsData||[];

        gantt.templates.leftside_text = (start, end, task) => {
          if (getTaskFitValue(task) === "left") {
            return this.resolveTemplateText(task.text)
          }
          return "";
        };
        const cfg = gantt.config;
        const strToDate = gantt.date.str_to_date(cfg.date_format, cfg.server_utc);

        gantt.templates.parse_date = function(date){
          return strToDate (date);
        };

        gantt.templates.rightside_text = (start, end, task) => {
          if (getTaskFitValue(task) === "right") {
            return this.resolveTemplateText(task.text)
          }
          return "";
        };
        gantt.templates.task_text = function taskTextTemplate(start, end, task) {
          if (getTaskFitValue(task) === "center") {
            return task.text;
          }
          return "";
        };
        gantt.templates.task_class = function (start, end, task) {
          if(task.type==='project'){
              return "task-project"
          }
          return "task-normal";
        };
        gantt.templates.grid_row_class = gantt.templates.task_row_class = function (start, end, task) {
          return "custom_row";
        };
        function getTaskFitValue(task) {
          const taskStartPos = gantt.posFromDate(task.start_date),
            taskEndPos = gantt.posFromDate(task.end_date)

          const width = taskEndPos - taskStartPos
          const textWidth = (task.text || '').length * gantt.config.font_width_ratio
          if (width <= textWidth) {
            const ganttLastDate = gantt.getState().max_date
            const ganttEndPos = gantt.posFromDate(ganttLastDate)
            if (ganttEndPos - taskEndPos <= textWidth) {
              return "left"
            }
            else {
              return "right"
            }
          }
          else {
            return "center";
          }
        }
      },
      ganttEvent(){
        gantt.attachEvent('onTaskSelected', (id) => {
          const task = gantt.getTask(id)
          this.$emit('task-selected', task)
        })
        gantt.attachEvent('onTaskIdChange', (id, new_id) => {
          if (gantt.getSelectedId() === new_id) {
            const task = gantt.getTask(new_id)
            this.$emit('task-selected', task)
          }
        })
        gantt.attachEvent("onTaskCreated", (task)=> {
          task.type=gantt.config.types.task
          task.id = null
          this.$emit('task-addModal',task);
          return false;
        });
        gantt.attachEvent("onAfterTaskDelete",(taskId)=>{
          this.$emit('task-deleted',taskId);
          gantt.render();
        });
        gantt.attachEvent("onLightbox", function (task_id){
          //any custom logic here
        });
        gantt.attachEvent("onBeforeLightbox", (id)=> {
          const task = gantt.getTask(id);
          this.$emit('task-addModal',task);
          return false;
        });
        gantt.attachEvent("onAfterTaskUpdate",(id,task)=>{
          gantt.render();
          return false;
        });
        gantt.attachEvent("onAfterTaskAdd",function(taskId,task){
          /*let parent = task.parent,
            types = gantt.config.types
          if (parent !== gantt.config.root_id && parent)  {
            const parentTask = gantt.getTask(parent)
            if(parentTask){
              if(parentTask.parent){
                const parentParentTask = gantt.getTask(parentTask.parent)
                if(parentParentTask&&parentParentTask.type===types.project){
                  parentTask.type = types.subproject;
                }else{
                  parentTask.type = types.project;
                }
              }else{
                parentTask.type = types.project;
              }
              gantt.updateTask(parentTask.id,parentTask);
            }
          }
          gantt.render();*/
        })
      },
      action(actionName){
        const action = this.actions[actionName]
        if (!action)
          return
        if (this.singularAction[actionName]) {
          action()
          return
        }
        const task_id = gantt.getSelectedId()
        if(!task_id){
        //   this.$message.warn("请先选择操作项")
          return
        }
        const indexes = {}
        const siblings = {}
        gantt.ext.undo.saveState(task_id, 'task')
        indexes[task_id] = gantt.getTaskIndex(task_id)
        siblings[task_id] = {
          first: null
        }
        let currentId = task_id
        while (gantt.isTaskExists(gantt.getPrevSibling(currentId)) && gantt.isSelectedTask(gantt.getPrevSibling(currentId))) {
          currentId = gantt.getPrevSibling(currentId)
        }
        siblings[task_id].first = currentId
        const updated = {}
        if (this.cascadeAction[actionName]) {
          if (!updated[gantt.getParent(task_id)]) {
            const updated_id = action(task_id, indexes, siblings)
            updated[updated_id] = true
          } else {
            updated[task_id] = true
          }
        } else {
          action(task_id, indexes)
        }
      },
      moveTask(task_id,direction){
        let index = gantt.getTaskIndex(task_id)
        if(index===0&&direction<0){
          return;
        }
        index = index+direction
        gantt.moveTask(task_id,index)
      },
      shiftTask(task_id, direction) {
        const task = gantt.getTask(task_id)
        task.start_date = gantt.date.add(task.start_date, direction, 'day')
        task.end_date = gantt.calculateEndDate({start_date:task.start_date, duration:task.duration})
        gantt.updateTask(task.id,task)
      },
    },
    created() {},
    mounted: function () {
      this.ganttConfig()
      this.ganttEvent()
      gantt.init(this.$refs.gantt)
      gantt.parse(this.taskData)
    }
  }
</script>
<style>
  .custom_row {
    background: rgb(245, 248, 245);
  }
  .task-normal{
    background-color: #80f3aa;
    color:#80f3aa;
  }
  .task-normal .gantt_task_progress {
    background: #67e595;
  }
  .task-project{
    background-color: #51a2f5;
    color:#51a2f5;
  }
  .task-project .gantt_task_progress {
    background: #1675d5;
  }
  .weekend {
    background: #f4f7f4 !important;
  }

  .gantt_selected .weekend {
    background: #FFF3A1 !important;
  }
  .gantt_grid_cell.grid_cell{
    transition: all .2s;
  }
</style>

